using System.Reflection;
using System.Reflection.Emit;
using System;
using System.Collections.Generic;


namespace Tracker.Data.DataTransfer
{
		/// <summary>
		/// This is a customizable extender class for the 'Task' data transfer object
		/// </summary>
	public partial class Task
	{
        private Dictionary<string, object> dictionary = new Dictionary<string, object>();

        private static Type ExtendedType { get; set; }

        /// <summary>
        /// Add new properties to Task
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="d"></param>
        /// <returns></returns>
        public dynamic Add<T>(Dictionary<string, object> d)
        {
            if (ExtendedType == null)
            {
                AssemblyBuilder assemblyBulder = AppDomain.CurrentDomain.DefineDynamicAssembly
                (new AssemblyName("WindowsFormsApplication3_datagrid"), AssemblyBuilderAccess.Run);
                ModuleBuilder moduleBuilder = assemblyBulder.DefineDynamicModule("dynamic");
                //TypeBuilder typeBuilder = moduleBuilder.DefineType(Guid.NewGuid().ToString());
                TypeBuilder typeBuilder = moduleBuilder.DefineType("ExtendedCalss");
                typeBuilder.SetParent(this.GetType());

                foreach (KeyValuePair<string, object> keyValuePair in d)
                {
                    PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(keyValuePair.Key,
                                                                           System.Reflection.PropertyAttributes.HasDefault,
                                                                           typeof(T), Type.EmptyTypes);
                    MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + keyValuePair.Key, MethodAttributes.Public, CallingConventions.HasThis,
                        typeof(T), Type.EmptyTypes);
                    ILGenerator getter = getMethodBuilder.GetILGenerator();
                    getter.Emit(OpCodes.Ldarg_0);
                    getter.Emit(OpCodes.Ldstr, keyValuePair.Key);

                    getter.Emit(OpCodes.Callvirt, typeof(Task).GetMethod("Get", BindingFlags.Instance | BindingFlags.Public).MakeGenericMethod(typeof(T)));
                    getter.Emit(OpCodes.Ret);
                    propertyBuilder.SetGetMethod(getMethodBuilder);

                    //set method
                    MethodBuilder setMethodBuilder = typeBuilder.DefineMethod("set_" + keyValuePair.Key, MethodAttributes.Public, CallingConventions.HasThis,
                  null, new Type[] { typeof(T) });

                    ILGenerator setter = setMethodBuilder.GetILGenerator();
                    setter.Emit(OpCodes.Ldarg_0);
                    setter.Emit(OpCodes.Ldstr, keyValuePair.Key);
                    setter.Emit(OpCodes.Ldarg_1);
                    //   setter.Emit(OpCodes.Ldstr,keyValuePair.Value as T);


                    setter.Emit(OpCodes.Callvirt, typeof(Task).GetMethod("Set", BindingFlags.Instance | BindingFlags.Public).MakeGenericMethod(typeof(T)));
                    setter.Emit(OpCodes.Ret);
                    propertyBuilder.SetSetMethod(setMethodBuilder);
                }


                Type type = typeBuilder.CreateType();
                ExtendedType = type;
            }



            dynamic sub = Activator.CreateInstance(ExtendedType);
            foreach (var item in this.GetType().GetProperties())
            {
                var newProperty = sub.GetType().GetProperty(item.Name,
                                                            BindingFlags.Instance | BindingFlags.Public |
                                                            BindingFlags.FlattenHierarchy);

                newProperty.SetValue(sub, item.GetValue(this, null), null);
            }

            sub.dictionary = this.dictionary;
            foreach (KeyValuePair<string, object> keyValuePair in d)
            {
                dictionary.Add(keyValuePair.Key, keyValuePair.Value);
            }


            return sub;

        }

        public T Get<T>(string key)
        {

            return (T)dictionary[key];


        }

        public void Set<T>(string key, T value)
        {
            if (dictionary[key] == null)
            {
                throw new Exception();
            }
            dictionary[key] = value;
        }
	}
}
